﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.ComponentModel;
using System.Resources;

namespace GenericValidation
{
    public interface IValidation
    {
        void Inherit<TBase>(IValidationFactory factory) where TBase : class;

        ValidationResult Validate(object item, params string[] tags);

        void LocalizeMembers(ResourceManager resourceManager);

        void LocalizeMessages(ResourceManager resourceManager);

        string LocalizeMessage(string templateKey);

        string LocalizeMember(string memberNameKey);

        void Register<T>(IValidator<T> rule) where T : class;
    }

    public interface IValidation<T> : IValidation
        where T : class
    {
        IValidator<T> IsValid(Expression<Func<T, bool>> expression);

        ValidationMember<T, U> Member<U>(Expression<Func<T, U>> expression);
    }
    
    public abstract class Validation<TEntity> : IValidation<TEntity>
        where TEntity:class
    {
        public ResourceManager FieldLocalization { get; set; }

        private List<ResourceManager> _messageLocalization = new List<ResourceManager>();

        public List<ResourceManager> MessageLocalization
        {
            get { return this._messageLocalization; }
        }

        public List<IValidator<TEntity>> Rules = new List<IValidator<TEntity>>();

        private List<IValidation> BaseItems = new List<IValidation>();

        #region IValidation Members

        public virtual void Inherit<TBase>(IValidationFactory factory)
            where TBase : class
        {
            this.BaseItems.Add(factory.Resolve<TBase>());
        }
        
        public virtual void LocalizeMembers(ResourceManager resourceManager)
        {
            this.FieldLocalization = resourceManager;
        }

        public virtual void LocalizeMessages(ResourceManager resourceManager)
        {
            this.MessageLocalization.Add(resourceManager);
        }

        public virtual string LocalizeMessage(string resourceKey)
        {
            var theResult = resourceKey;
            foreach (ResourceManager res in this.MessageLocalization)
            {
                var resValue = res.GetString(resourceKey);
                if (string.IsNullOrEmpty(resValue) == false)
                {
                    theResult = resValue;
                    break;
                }
            }

            return theResult;
        }

        public virtual string LocalizeMember(string resourceKey)
        {
            var theResult = resourceKey;
            if (this.FieldLocalization != null)
            {
                var resourceValue = this.FieldLocalization.GetString(resourceKey);
                if (string.IsNullOrEmpty(resourceValue) == false)
                {
                    theResult = resourceValue;
                }
            }

            return theResult;
        }

        public ValidationResult Validate(object item, params string[] tags)
        {

            var theResult = new ValidationResult()
            {
                IsValid = true,
            };

            //validating the Item with the Base Validation(s)
            foreach (var validation in this.BaseItems)
            {
                var baseResult = validation.Validate(item, tags);
                theResult.Add(baseResult);
            }

            var errorInfo = theResult.ErrorInfo != null
                ? theResult.ErrorInfo.ToList()
                : new List<string>();

            if (item is TEntity)
            {
                foreach (var rule in this.Rules)
                {
                    bool ruleIsValid = rule.Eval(item as TEntity, tags);
                    if (!ruleIsValid)
                    {
                        errorInfo.AddRange(rule.GetErrorMessage(item as TEntity));
                        theResult.IsValid = false;
                    }
                }

                theResult.ErrorInfo = errorInfo.ToArray();
            }
            else
            {
                var message = string.Format("Validation argument {0} is of type {1}", item, typeof(TEntity).Name);
                throw new ArgumentException(message, "item");
            }

            return theResult;
        }

        public void Register(IValidator<TEntity> validator)
        {
            this.Rules.Add(validator);
        }

        void IValidation.Register<U>(IValidator<U> validator)
        {
            if (typeof(U) == typeof(TEntity))
            {
                this.Rules.Add((IValidator<TEntity>)validator);
            }
        }

        #endregion

        #region IValidation<TEntity> Members

        public virtual IValidator<TEntity> IsValid(Expression<Func<TEntity, bool>> expression)
        {
            var theResult = new Validator<TEntity>(this as IValidation, expression);
            return theResult;
        }

        public virtual ValidationMember<TEntity, U> Member<U>(Expression<Func<TEntity, U>> expression)
        {
            var theResult = new ValidationMember<TEntity, U>(this as IValidation, expression);
            theResult.Localize(theResult.MemberName);

            return theResult;
        }

        #endregion
    }
}
